'''
Created on 6-Jun-09

@author: Matt
'''
import __builtin__

import urllib, urllib2
import socket
from urllib2 import URLError

#default timeout in seconds
socket.setdefaulttimeout(7)

def google(q, hl="en", rsz="small", start="0"):
    """ Returns the first hit on google with the given query. """
    google = 'http://ajax.googleapis.com/ajax/services/search/web?v=1.0&' 
    query = urllib.urlencode({'rsz':rsz, 'hl':hl, 'start':start, 'q':q})
    url = ''.join((google, query))
    res = get_html(url)
    if res is None:
        return None
    start, end = find_block(res, '"url":"', '",')
    return urllib.unquote(res[start:end])

def unescape(text, hex):
    f = '&#x' if hex else '&#'
    flen = len(f)
    start = 0
    while start < len(text):
        start = text.find(f, start)
        if start == -1:
            return text
        end = text.find(';', start+flen)
        if end == -1:
            return text
        u = text[start+flen:end]
        try:
            ch = unichr(int(u,16)) if hex else chr(int(u))
            ch = ch.encode('iso-8859-15', 'replace')
        except ValueError:
            ch = '?'
        text = ''.join((text[:start], ch, text[end+1:]))
    return text

def get_html(url):
    """ Attempts to retrieve the HTML source of the given URL. """
    try:
        req = urllib2.Request(url)
        result = urllib2.urlopen(url)
        htmlSource = result.read()
        result.close()
    except URLError:
        return None
    return htmlSource

def find_block(text, startStr, endStr, startIndex=0):
    """ 
    Returns the start and end indices between the given substrings. Example::
        txt = '<b>Blah Blah</b>'
        start, end = find_block(txt, '<b>', '</b>')
        print txt[start:end] 
        => Blah Blah
    Returns (-1, -1) on failure.
    """
    start = text.find(startStr, startIndex)
    if start == -1:
        return (-1, -1)
    start += len(startStr)
    end = text.find(endStr, start)
    if end == -1:
        return (-1, -1)
    return (start, end)

class Plugin:
    """ 
    Plugin is the base protocol for all plugins.
    Subclasses should implement the list() and search() methods,
    and the following variables: 
    header, name, empty and (optionally) description.
    """
    
    def __init__(self):
        """ Resets this plugins values to their defaults. """
        self.fetching = False
        self.header = [] 
        """ 
        Returns a list of column header names. 
        Example implementation::
            return ["Title", "Rating", "# Of Votes", "Source"]
        """
        self.name = ""
        """ The name of this plugin. """
        self.description = ""
        """ A description of this plugin. """
        self.lastQuery = ""
        """ The last query used with this plugin. """
        self.display = False
        """ 
        Initially False, this will tell the engine whether or not
        to display the data. This should be set to True when
        the result data is found.
        """
        
    def list(self):
        """ 
        Returns a list of results corresponding to the header() 
        data. This may look like::
            return [self.title, self.rating, self.votes, self.source]
        
        If a search has not been made or if it has been cleared, you should
        return None.
        """
        return None
    
    def search(self, str, options=None):
        """ 
        Queries the search engine with the given string for
        a new set of results. Options is an optional parameter
        used for specifying the options for certain sites.
        """
        pass
    
    def __str__(self):
        return ':'.join((self.name, str(self.list())))

class PluginManager:
    """ 
    Manages all active plugins, can be accessed 
    by the builtin plugins variable. Example of use::
        class IMDB(Plugin):
            ...
        
        imdb = IMDB()
        plugins.group("Movies").append(imdb)
    """
    
    def __init__(self):
        self.groups = {}
        self.default_group = None
        self.options = {}
        
    def get(self, group, name):
        """
        Gets the first plugin by name in the given group.
        """
        for p in group:
            if p.name == name:
                return p
        return None
                
    def group(self, name):
        """ 
        Returns the group by `name` or a newly created group
        if not found. Note that group names are unique, and are
        used as identifiers in the executable program parameters.
        """
        g = self.groups.get(name)
        if g is None:
            self.groups[name] = []
        return self.groups[name]
        
    def __str__(self):
        s = 'plugins:{ '
        if len(self.groups) == 0:
            return ''.join((s, '}'))
        for v, k in self.groups.iteritems():
            s = ''.join((s, v, '(', str(len(k)), '), '))
        s = s if not s.endswith('), ') else s[:-2]
        return ''.join((s, ' }'))

__builtin__.plugins = PluginManager()
